package com.tiancheng.trade.payment.service.impl;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.NumberUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tiancheng.commom.pay.PayOrderAutoQueryConfig;
import com.tiancheng.trade.commom.core.data.PageInfo;
import com.tiancheng.trade.commom.core.data.Result;
import com.tiancheng.trade.commom.core.enums.*;
import com.tiancheng.trade.commom.core.exception.Asserts;
import com.tiancheng.trade.commom.core.exception.BusinessException;
import com.tiancheng.trade.commom.core.exception.error.BusinessErrorInfoEnum;
import com.tiancheng.trade.commom.core.utils.JsonUtil;
import com.tiancheng.trade.commom.web.cache.SettleRulesCache;
import com.tiancheng.trade.commom.web.cache.model.MerchantBO;
import com.tiancheng.trade.commom.web.model.request.OrderPayDTO;
import com.tiancheng.trade.commom.web.model.request.VerificationRequestVO;
import com.tiancheng.trade.commom.web.model.response.MerchantChannelResponseDTO;
import com.tiancheng.trade.commom.web.model.response.SettlementRuleResVO;
import com.tiancheng.trade.commom.web.model.response.VerificationOrderResponseVO;
import com.tiancheng.trade.payment.bo.OrgPaymentChannelDetailBO;
import com.tiancheng.trade.payment.bo.cmq.ApplePayResultCheckBO;
import com.tiancheng.trade.payment.bo.meesage.PaymentCommonMessageBO;
import com.tiancheng.trade.payment.bo.notify.PayNotifyBO;
import com.tiancheng.trade.payment.bo.notify.PayNotifySubBO;
import com.tiancheng.trade.payment.channel.AbstractPayChannel;
import com.tiancheng.trade.payment.channel.PayChannelFactory;
import com.tiancheng.trade.payment.channel.apple.ApplePayChannel;
import com.tiancheng.trade.payment.channel.bo.*;
import com.tiancheng.trade.payment.channel.enums.ChannelOrderStatusEnum;
import com.tiancheng.trade.payment.enums.FlowStatusEnum;
import com.tiancheng.trade.commom.web.model.request.MerchantChannelRequestDTO;
import com.tiancheng.trade.payment.mapper.PaymentOrderMapper;
import com.tiancheng.trade.payment.model.*;
import com.tiancheng.trade.payment.mq.PaymentCenterMsgProducer;
import com.tiancheng.trade.payment.service.*;
import com.tiancheng.trade.payment.util.EnumUtil;
import com.tiancheng.trade.payment.util.ExcelBuildUtil;
import com.tiancheng.trade.payment.util.ObjectConvertUtil;
import com.tiancheng.trade.payment.util.cache.PaymentCache;
import com.tiancheng.trade.payment.util.helper.PaymentIdHelper;
import com.tiancheng.trade.payment.util.lock.Lock;
import com.tiancheng.trade.payment.vo.admin.PaymentFlowQueryVo;
import com.tiancheng.trade.payment.vo.admin.res.PaymentFlowQueryResVO;
import com.tiancheng.trade.payment.vo.api.*;
import com.tiancheng.trade.commom.web.model.response.UnifiedOrderResVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

import jakarta.servlet.http.HttpServletResponse;
import org.springframework.transaction.support.TransactionTemplate;

import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 支付流水服务
 *
 * @author su
 */
@Service
@Slf4j
public class PaymentOrderServiceImpl extends ServiceImpl<PaymentOrderMapper, PaymentOrder> implements PaymentOrderService {

    @Resource
    private PaymentOrderMapper paymentOrderMapper;
    @Resource
    private PaymentChannelService paymentChannelService;
    @Resource
    private PaymentWayService paymentWayService;
    @Resource
    private PaymentFlowDetailService paymentFlowDetailService;
    @Resource
    @Lazy
    private NotifyService notifyService;
    @Resource
    private OrgPaymentChannelService orgPaymentChannelService;
    @Resource
    private SettlePlatformService settlePlatformService;
    @Resource
    private PaymentFlowDetailSettleService flowDetailSettleService;
    @Resource
    private IPaymentFlowDetailSettleDetailService flowDetailSettleDetailService;
    @Resource
    private TransactionTemplate transactionTemplate;
    @Resource
    private SettleRulesCache settleRulesCache;
    @Resource
    private PaymentCenterMsgProducer paymentCenterMsgProducer;

    @Override
    public UnifiedOrderResVO unifiedOrder(OrderPayDTO unifiedOrderVO) {
        final OrgPaymentChannelDetailBO orgPaymentChannelDetailBO = orgPaymentChannelService.queryAllOrgPaymentChannelInfoById(unifiedOrderVO.getOrgPaymentChannelId());
        // 检查支付渠道、方式是否匹配
        BusinessErrorInfoEnum.CHANNEL_NO_MATCH.assertIsTrue(Objects.nonNull(orgPaymentChannelDetailBO) && StringUtils.isNotEmpty(orgPaymentChannelDetailBO.getPaymentChannelCode()));
        BusinessErrorInfoEnum.CHANNEL_NO_PAY_WAY.assertNotNull(PaymentWayEnum.getByCode(unifiedOrderVO.getPayWay()));
        BusinessErrorInfoEnum.CHANNEL_NO_PAY_SCENE.assertNotNull(EnumUtil.getEnumByCodeIgnoreCase(PaySceneEnum.class, unifiedOrderVO.getPayScene()));
        try (Lock lock = new Lock(Lock.LOCK_CHANNEL_PAY, unifiedOrderVO.getPaymentId(), 0, 5)) {
            BusinessErrorInfoEnum.CHANNEL_PAY_DUP.assertIsTrue(lock.getLockRes());

            QueryWrapper<PaymentOrder> conditions = new QueryWrapper<>();
            conditions.eq("payment_id", unifiedOrderVO.getPaymentId());
            conditions.eq("pay_status", FlowStatusEnum.SUCCESS.getCode());
            BusinessErrorInfoEnum.CHANNEL_PAY_DUP.assertIsTrue(paymentOrderMapper.selectCount(conditions) == 0);
            String flowId = PaymentIdHelper.getPay(unifiedOrderVO.getUid(), unifiedOrderVO.getPayChannel(), null, orgPaymentChannelDetailBO.getChannelOrderIdPre());


            PaymentOrder paymentOrder = ObjectConvertUtil.convert(unifiedOrderVO, PaymentOrder.class).get();
            unifiedOrderVO.getSubOrderPayDTOS().forEach(unifiedSubOrderVO -> {
                if (null == unifiedSubOrderVO.getPlatformDiscount()) {
                    unifiedSubOrderVO.setPlatformDiscount(unifiedSubOrderVO.getOrderAmount() - unifiedSubOrderVO.getAmount());
                }
            });
            paymentOrder.setUserId(unifiedOrderVO.getUid());
            paymentOrder.setFlowId(flowId);
            paymentOrder.setPayStatus(FlowStatusEnum.WAITING.getCode());
            paymentOrder.setPaymentChannel(unifiedOrderVO.getPayChannel());
            paymentOrder.setPaymentWay(unifiedOrderVO.getPayWay());
            paymentOrder.setOrderApp(unifiedOrderVO.getBusinessType());
            paymentOrder.setUserTel(unifiedOrderVO.getUserTel());
            paymentOrder.setDiscount(unifiedOrderVO.getDiscount());
            paymentOrder.setSlottingRate(this.getSlottingRate(unifiedOrderVO.getPayChannel(), unifiedOrderVO.getPayWay()));
            paymentOrder.setPlatformDiscount(unifiedOrderVO.getDiscount());

            Map<String, SettlementRuleResVO> merchantRuleMap = settleRulesCache.getRuleByMerchantCodes(unifiedOrderVO.getOrgPaymentChannelId(),
                    unifiedOrderVO.getSubOrderPayDTOS().stream().map(OrderPayDTO.SubOrderPayDTO::getMerchantCode).collect(Collectors.toList()));
            BusinessErrorInfoEnum.CHANNEL_PAY_FAIL.assertIsTrue(null != merchantRuleMap && merchantRuleMap.size() > 0, "商户清分信息配置异常！");

            // 保存支付流水、支付流水明细
            final List<PaymentFlowDetail> flowDetails = convertPaymentFlowDetails(paymentOrder, merchantRuleMap, unifiedOrderVO);
            // 渠道支付
            AbstractPayChannel payChannel = PayChannelFactory.get(unifiedOrderVO.getPayChannel());
            UnifiedOrderBO unifiedOrderBO = this.getUnifiedOrderBO(unifiedOrderVO, flowDetails, merchantRuleMap, orgPaymentChannelDetailBO);

            for (PaymentFlowDetail flowDetail : flowDetails) {
                Optional<UnifiedOrderBO.SubOrderBO> first = unifiedOrderBO.getSubOrders().stream().filter(s -> s.getSubOrderId().equals(flowDetail.getSubOrderId()) && s.getSubFlowId().equals(flowDetail.getSubFlowId())).findFirst();
                if (first.isPresent()) {
                    flowDetail.setMerchantRegId(first.get().getMerchantRegId());
                    flowDetail.setSubMerchantId(first.get().getSubMerchantId());
                    flowDetail.setEnterpriseCode(first.get().getEnterpriseCode());
                }
            }

            paymentOrderMapper.insert(paymentOrder);
            paymentFlowDetailService.saveBatch(flowDetails);
            unifiedOrderBO.setOrderId(flowId);      //使用支付流水号作为统一下单号
            unifiedOrderBO.setFlowId(flowId);
            unifiedOrderBO.setPaymentChannelName(orgPaymentChannelDetailBO.getPaymentChannelName());
            unifiedOrderBO.setSlottingRate(orgPaymentChannelDetailBO.getSlottingRate());
            unifiedOrderBO.setChannelOrderIdPre(orgPaymentChannelDetailBO.getChannelOrderIdPre());

            // 调用渠道接口发起支付请求
            Result<UnifiedOrderResBO> result = payChannel.unifiedOrder(unifiedOrderBO);
            BusinessErrorInfoEnum.CHANNEL_PAY_FAIL.assertIsTrue(result.getRet() == 0, result.getMsg());
            // 更新渠道单号或支付失败
            handleOrderUpdate(paymentOrder, result);
            // 生成结算信息
            flowDetailSettleService.genSettle(paymentOrder, flowDetails, unifiedOrderBO.getSubSettles());
            // 此方法没有事务，可以直接抛出支付失败异常

            sendQueryOrderStatusMsg(unifiedOrderVO, flowId, payChannel);
            UnifiedOrderResVO unifiedOrderResVO = new UnifiedOrderResVO();
            unifiedOrderResVO.setFlowId(flowId);
            unifiedOrderResVO.setPaymentId(unifiedOrderVO.getPaymentId());
            unifiedOrderResVO.setAmount(unifiedOrderVO.getAmount());
            unifiedOrderResVO.setPayInfo(result.getData().getPayInfo());
            unifiedOrderResVO.setSlottingRate(paymentOrder.getSlottingRate());
            return unifiedOrderResVO;
        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            throw new BusinessException(BusinessErrorInfoEnum.CHANNEL_PAY_FAIL, null, null, e);
        }
    }

    /**
     * 组装子订单信息
     *
     * @param paymentOrder    主订单
     * @param merchantRuleMap 商户清分信息
     * @param unifiedOrderVO  支付参数
     * @return
     */
    private List<PaymentFlowDetail> convertPaymentFlowDetails(PaymentOrder paymentOrder, Map<String, SettlementRuleResVO> merchantRuleMap
            , OrderPayDTO unifiedOrderVO) {
        List<PaymentFlowDetail> flowDetails = new ArrayList<>();
        int subFlowInc = 1; // 子订单流水起始值
        for (OrderPayDTO.SubOrderPayDTO subOrder : unifiedOrderVO.getSubOrderPayDTOS()) {
            PaymentFlowDetail flowDetail = new PaymentFlowDetail();
            BeanUtils.copyProperties(subOrder, flowDetail);
            flowDetail.setFlowId(paymentOrder.getFlowId());
            flowDetail.setPaymentId(paymentOrder.getPaymentId());
            SettlementRuleResVO merchantRules = merchantRuleMap.get(subOrder.getMerchantCode());
            BusinessErrorInfoEnum.CHANNEL_PAY_FAIL.assertIsTrue(null != merchantRules);
            String settleRuleType = merchantRules.getSettleRuleType().equals(SettleRuleTypeEnums.merchant.getCode())
                    ? SettleRuleTypeEnums.merchant.getCode() : SettleRuleTypeEnums.product.getCode();
            flowDetail.setSettleRuleCode(settleRuleType + merchantRules.getSettleRuleCode());
            // 子订单优惠金额
            flowDetail.setPlatformDiscount(null == subOrder.getPlatformDiscount() ? 0 : subOrder.getPlatformDiscount());
            flowDetail.setDiscount(flowDetail.getPlatformDiscount());
            // 子订单流水
            flowDetail.setSubFlowId(PaymentIdHelper.getSubOrderFlowId(paymentOrder.getFlowId(), subFlowInc));
            flowDetail.setCouponSettleStatus("");
            flowDetails.add(flowDetail);
            subFlowInc++;
        }
        return flowDetails;
    }

    /**
     * 查询支付订单状态，发送到延迟队列
     *
     * @param unifiedOrderVO
     * @param flowId
     * @param payChannel
     */
    private void sendQueryOrderStatusMsg(OrderPayDTO unifiedOrderVO, String flowId, AbstractPayChannel payChannel) {
        // 判断是否需要主动查询订单状态
        if (payChannel.autoQueryStatus(unifiedOrderVO.getOrgPaymentChannelId())) {
            // 将查询参数放入队列
            PayOrderAutoQueryConfig orderAutoQueryConfig = payChannel.getOrderAutoQueryConfig(unifiedOrderVO.getOrgPaymentChannelId());
            if (Objects.nonNull(orderAutoQueryConfig) && orderAutoQueryConfig.getEnable() && orderAutoQueryConfig.getRetryCount() != 0) {
                if (orderAutoQueryConfig.getRetryInterval() <= 3000) {
                    orderAutoQueryConfig.setRetryInterval(3000);
                }
                if (orderAutoQueryConfig.getRetryCount() >= 100) {
                    orderAutoQueryConfig.setRetryCount(100);
                }
                PaymentCommonMessageBO messageBO = new PaymentCommonMessageBO();
                messageBO.setMessageType(MessageTypeEnum.trade_payment_common_query_pay_result);
                messageBO.setRetryCount(orderAutoQueryConfig.getRetryCount());
                messageBO.setRetryInterval(orderAutoQueryConfig.getRetryInterval());
                messageBO.setCurrentRetryPosition(1);

                GetUnifiedOrderVo getUnifiedOrderVo = new GetUnifiedOrderVo();
                getUnifiedOrderVo.setFlowId(flowId);
                messageBO.setMessageBody(JsonUtil.toJsonString(getUnifiedOrderVo));
                paymentCenterMsgProducer.setPaymentQueryMsg(messageBO);
                PaymentCache.cachePaymentStatusQueryParams(flowId);
                log.info("主动查询订单支付状态，存入延迟消息队列：{}", JsonUtil.toJsonString(messageBO));
            }
        }
    }

    @Override
    public UnifiedOrderResVO deductionUnified(OrderPayDTO unifiedOrderVO) {
        // 检查支付渠道、方式是否匹配
        BusinessErrorInfoEnum.CHANNEL_NO_MATCH.assertIsTrue(PaymentChannelEnum.UMS.getCode().equals(unifiedOrderVO.getPayChannel()));
        BusinessErrorInfoEnum.CHANNEL_NO_PAY_WAY.assertIsTrue(unifiedOrderVO.getPayWay().equalsIgnoreCase(PaymentWayEnum.DEDUCTION.getCode()));
        BusinessErrorInfoEnum.CHANNEL_NO_PAY_SCENE.assertNotNull(EnumUtil.getEnumByCodeIgnoreCase(PaySceneEnum.class, unifiedOrderVO.getPayScene()));
        try (Lock lock = new Lock(Lock.LOCK_CHANNEL_PAY, unifiedOrderVO.getPaymentId(), 0, 5)) {
            BusinessErrorInfoEnum.CHANNEL_PAY_DUP.assertIsTrue(lock.getLockRes());

            QueryWrapper<PaymentOrder> conditions = new QueryWrapper<>();
            conditions.eq("payment_id", unifiedOrderVO.getPaymentId());
            conditions.eq("pay_status", FlowStatusEnum.SUCCESS.getCode());
            BusinessErrorInfoEnum.CHANNEL_PAY_DUP.assertIsTrue(paymentOrderMapper.selectCount(conditions) == 0);
            final OrgPaymentChannelDetailBO orgPaymentChannelDetailBO = orgPaymentChannelService.queryAllOrgPaymentChannelInfoById(unifiedOrderVO.getOrgPaymentChannelId());
            String flowId = PaymentIdHelper.getPay(unifiedOrderVO.getUid(), unifiedOrderVO.getPayChannel(), "notPay", orgPaymentChannelDetailBO.getChannelOrderIdPre());

            PaymentOrder paymentOrder = ObjectConvertUtil.convert(unifiedOrderVO, PaymentOrder.class).get();
            unifiedOrderVO.getSubOrderPayDTOS().forEach(unifiedSubOrderVO -> {
                unifiedSubOrderVO.setPlatformDiscount(unifiedSubOrderVO.getOrderAmount());
                unifiedSubOrderVO.setAmount(0);
            });
            paymentOrder.setUserId(unifiedOrderVO.getUid());
            paymentOrder.setFlowId(flowId);
            paymentOrder.setPayStatus(FlowStatusEnum.WAITING.getCode());
            paymentOrder.setPaymentChannel(unifiedOrderVO.getPayChannel());
            paymentOrder.setPaymentWay(unifiedOrderVO.getPayWay());
            paymentOrder.setOrderApp(unifiedOrderVO.getBusinessType());
            paymentOrder.setUserTel(unifiedOrderVO.getUserTel());
            paymentOrder.setDiscount(paymentOrder.getOrderAmount());
            paymentOrder.setSlottingRate(this.getSlottingRate(unifiedOrderVO.getPayChannel(), unifiedOrderVO.getPayWay()));
            paymentOrder.setPlatformDiscount(paymentOrder.getOrderAmount());
            paymentOrder.setChannelDiscount(0);
            paymentOrder.setAmount(0);

            paymentOrderMapper.insert(paymentOrder);
            final Map<String, SettlementRuleResVO> merchantRuleMap = settleRulesCache.querySettleRules(unifiedOrderVO.getOrgPaymentChannelId(),
                    unifiedOrderVO.getSubOrderPayDTOS().stream().map(OrderPayDTO.SubOrderPayDTO::getMerchantCode).collect(Collectors.toList()));
            BusinessErrorInfoEnum.CHANNEL_PAY_FAIL.assertIsTrue(null != merchantRuleMap && merchantRuleMap.size() > 0);
            paymentFlowDetailService.save(flowId, paymentOrder.getPaymentId(), unifiedOrderVO, merchantRuleMap);
            UnifiedOrderResVO unifiedOrderResVO = new UnifiedOrderResVO();
            unifiedOrderResVO.setFlowId(flowId);
            unifiedOrderResVO.setPaymentId(unifiedOrderVO.getPaymentId());
            unifiedOrderResVO.setAmount(unifiedOrderVO.getAmount());
            unifiedOrderResVO.setPayInfo("");
            unifiedOrderResVO.setSlottingRate(paymentOrder.getSlottingRate());

            // 构建支付成功通知
            final PayResultBO bo = new PayResultBO();
            bo.setUid(unifiedOrderVO.getUid());
            bo.setOrderId(flowId);
            bo.setAmount(0);
            bo.setPayTime(LocalDateTime.now());
            bo.setStatus(FlowStatusEnum.SUCCESS.getCode());
            bo.setCouponAmount(0);

            bo.setTradeNo(flowId);
            notifyService.mockPayNotify(bo);
            return unifiedOrderResVO;
        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            throw new BusinessException(BusinessErrorInfoEnum.CHANNEL_PAY_FAIL, null, e.getMessage(), e);
        }
    }

    /**
     * 更新渠道单号或支付失败
     *
     * @param paymentOrder
     * @param result
     */
    private void handleOrderUpdate(PaymentOrder paymentOrder, Result<UnifiedOrderResBO> result) {
        PaymentOrder paymentOrderUpdate = new PaymentOrder();
        QueryWrapper<PaymentOrder> updateConditions = new QueryWrapper<>();
        updateConditions.eq("flow_id", paymentOrder.getFlowId());
        updateConditions.eq("user_id", paymentOrder.getUserId());
        if (result.getRet() != 0) {
            paymentOrderUpdate.setPayStatus(FlowStatusEnum.FAIL.getCode());
            paymentOrderMapper.update(paymentOrderUpdate, updateConditions);
        } else if (result.getRet() == 0 && (null != result.getData() && (StringUtils.isNotEmpty(result.getData().getTransactionId()) ||
                StringUtils.isNotEmpty(result.getData().getOutTradeNo())))) {
            // 招行渠道更新“渠道订单号”信息
            paymentOrderUpdate.setTradeNo(result.getData().getOutTradeNo());
            paymentOrderUpdate.setTransactionId(result.getData().getTransactionId());
            paymentOrderMapper.update(paymentOrderUpdate, updateConditions);
        }
    }

    /**
     * 获取支付渠道的通道费率
     *
     * @param payChannel
     * @param payWay
     * @return
     */
    private BigDecimal getSlottingRate(String payChannel, String payWay) {
        // 如果支付渠道是直连自营，到支付方式表拿通道费率，否则到支付渠道表拿
        if (PaymentChannelEnum.APPLE.getCode().equals(payChannel)) {
            // 苹果支付，不用管，其实可以把apple 渠道加到PaymentChannel表里面。。。。。
            return new BigDecimal(30);
        }
        if (PaymentChannelEnum.SELF.getCode().equals(payChannel)) {
            PaymentWay paymentWay = paymentWayService.get(payChannel, payWay);
            if (paymentWay != null) {
                return paymentWay.getSlottingRate();
            }
        } else {
            PaymentChannel paymentChannel = null;
            try {
                final Optional<PaymentChannel> cachedPaymentChannel = PaymentCache.getCachedPaymentChannel(payChannel);
                if (cachedPaymentChannel.isPresent()) {
                    return cachedPaymentChannel.get().getSlottingRate();
                }
                paymentChannel = paymentChannelService.getByPaymentChannelCode(payChannel);
                if (paymentChannel != null) {
                    PaymentCache.cachePaymentChannel(payChannel, paymentChannel);
                }
            } catch (Exception e) {
                log.error("获取支付渠道信息异常:", e);
                paymentChannel = paymentChannelService.getByPaymentChannelCode(payChannel);
            }
            if (paymentChannel != null) {
                return paymentChannel.getSlottingRate();
            }
        }
        return null;
    }

    /**
     * 获取统一下单数据
     *
     * @param unifiedOrderVO
     * @return
     */
    private UnifiedOrderBO getUnifiedOrderBO(OrderPayDTO unifiedOrderVO, List<PaymentFlowDetail> flowDetails, Map<String,
            SettlementRuleResVO> merchantRuleMap, OrgPaymentChannelDetailBO orgPaymentChannelDetailBO) {
        UnifiedOrderBO unifiedOrderBO = new UnifiedOrderBO();
        unifiedOrderBO.setUid(unifiedOrderVO.getUid());
        unifiedOrderBO.setBusinessType(unifiedOrderVO.getBusinessType());
        unifiedOrderBO.setAmount(unifiedOrderVO.getAmount());
        unifiedOrderBO.setOrderAmount(unifiedOrderVO.getOrderAmount());
        unifiedOrderBO.setPayWay(unifiedOrderVO.getPayWay());
        unifiedOrderBO.setOrderName(unifiedOrderVO.getOrderName());
        unifiedOrderBO.setPayScene(unifiedOrderVO.getPayScene());
        unifiedOrderBO.setTs(unifiedOrderVO.getTs());
        unifiedOrderBO.setUserTel(unifiedOrderVO.getUserTel());
        unifiedOrderBO.setSubOrders(new ArrayList<>());
        unifiedOrderBO.setOpenid(unifiedOrderVO.getOpenid());
        unifiedOrderBO.setSubAppId(unifiedOrderVO.getSubAppId());
        unifiedOrderBO.setEndPage(unifiedOrderVO.getEndPage());
        unifiedOrderBO.setSpInfo(unifiedOrderVO.getSpInfo());
        unifiedOrderBO.setMerchantCode(unifiedOrderVO.getMerchantCode());
        unifiedOrderBO.setMerchantName(unifiedOrderVO.getMerchantName());
        unifiedOrderBO.setGoodsTag(unifiedOrderVO.getGoodsTag());
        unifiedOrderBO.setReturnUrl(unifiedOrderVO.getReturnUrl());
        unifiedOrderBO.setTransactionId(unifiedOrderVO.getTransactionId());
        unifiedOrderBO.setReceiptData(unifiedOrderVO.getReceiptData());
        unifiedOrderBO.setOrgPaymentChannelId(unifiedOrderVO.getOrgPaymentChannelId());
        unifiedOrderBO.setPayExpiryTime(unifiedOrderVO.getPayExpiryTime());

        // 机构的支付渠道信息
        unifiedOrderBO.setOrgPaymentChannelId(unifiedOrderVO.getOrgPaymentChannelId());
        unifiedOrderBO.setPaymentChannelCode(unifiedOrderVO.getPayChannel());
        unifiedOrderBO.setBusinessCode(unifiedOrderVO.getBusinessCode());

        // 商户进件号处理-1-主商户
        Map<String, MerchantBO> merchantMap = querySettleMerchant(unifiedOrderVO);
        // 平台分账收款子商户
        Map<Long, MerchantChannelResponseDTO> orgPayMerchant = queryOrgPaymentMerchant(unifiedOrderVO);
        unifiedOrderVO.getSubOrderPayDTOS().forEach(subOrderVO -> {
            UnifiedOrderBO.SubOrderBO subOrderBO = new UnifiedOrderBO.SubOrderBO();
            subOrderBO.setMerchantCode(subOrderVO.getMerchantCode());
            subOrderBO.setMerchantName(subOrderVO.getMerchantName());
            subOrderBO.setProductCode(subOrderVO.getProductCode());
            subOrderBO.setProductId(subOrderVO.getProductId());
            subOrderBO.setProductName(subOrderVO.getProductName());
            subOrderBO.setProductDetail(subOrderVO.getProductDetail());
            subOrderBO.setProductCount(subOrderVO.getProductCount());
            subOrderBO.setAmount(subOrderVO.getAmount());
            subOrderBO.setOrderAmount(subOrderVO.getOrderAmount());
            subOrderBO.setSubOrderId(subOrderVO.getSubOrderId());
            if (!CollectionUtils.isEmpty(flowDetails)) {
                flowDetails.stream().filter(f -> f.getSubOrderId().equals(subOrderVO.getSubOrderId())).findFirst().ifPresent(f -> {
                    subOrderBO.setSubFlowId(f.getSubFlowId());
                });
            }
            if (merchantMap.containsKey(subOrderVO.getMerchantCode())) {
                subOrderBO.setSubMerchantId(merchantMap.get(subOrderVO.getMerchantCode()).getSubAppid());
                subOrderBO.setMerchantRegId(merchantMap.get(subOrderVO.getMerchantCode()).getMerchantRegId());
            }
            unifiedOrderBO.getSubOrders().add(subOrderBO);
        });
        handleSettle(unifiedOrderBO, merchantMap, merchantRuleMap, orgPayMerchant, orgPaymentChannelDetailBO);
        return unifiedOrderBO;
    }

    /**
     * 查询 清分子商户信息
     *
     * @param unifiedOrderVO 下单请求
     * @return
     */
    private Map<String, MerchantBO> querySettleMerchant(OrderPayDTO unifiedOrderVO) {
        List<MerchantChannelRequestDTO> merchants = new ArrayList<>();
        for (OrderPayDTO.SubOrderPayDTO subOrder : unifiedOrderVO.getSubOrderPayDTOS()) {
            MerchantChannelRequestDTO mcrd = new MerchantChannelRequestDTO();
            mcrd.setMerchantCode(subOrder.getMerchantCode());
            mcrd.setPaymentChannelCode(unifiedOrderVO.getPayChannel());
            mcrd.setOrgPaymentChannelId(unifiedOrderVO.getOrgPaymentChannelId());
            mcrd.setBusinessCode(unifiedOrderVO.getBusinessCode());
            merchants.add(mcrd);
        }
        return settlePlatformService.getMerchantInfo(merchants);
    }

    /**
     * 查询 清分平台分账子商户
     *
     * @param unifiedOrderVO 下单请求
     * @return
     */
    private Map<Long, MerchantChannelResponseDTO> queryOrgPaymentMerchant(OrderPayDTO unifiedOrderVO) {
        List<MerchantChannelResponseDTO> orgSubMerchant = settlePlatformService.queryOrgSubMerchant(Collections.singletonList(unifiedOrderVO.getOrgPaymentChannelId()));
        Asserts.isTrue(CollectionUtils.isNotEmpty(orgSubMerchant), "未设置平台分账收款子商户！");
        HashMap<Long, MerchantChannelResponseDTO> result = new HashMap<>();
        result.put(unifiedOrderVO.getOrgPaymentChannelId(), orgSubMerchant.get(0));
        return result;
    }


    /**
     * 处理分账信息
     */
    private void handleSettle(UnifiedOrderBO unifiedOrderBO, Map<String, MerchantBO> merchantMap,
                              Map<String, SettlementRuleResVO> merchantRuleMap, Map<Long, MerchantChannelResponseDTO> orgPayMerchant,
                              OrgPaymentChannelDetailBO orgPaymentChannelDetailBO) {
        // 结算信息
        final List<UnifiedOrderSettleBO> subSettleBOS = new ArrayList<>();
        // 平台收款子商户
        MerchantChannelResponseDTO platformSubMerchant = orgPayMerchant.get(unifiedOrderBO.getOrgPaymentChannelId());
        for (UnifiedOrderBO.SubOrderBO subOrder : unifiedOrderBO.getSubOrders()) {
            final SettlementRuleResVO merchantRule = merchantRuleMap.get(subOrder.getMerchantCode());
            final MerchantBO merchantBO = merchantMap.get(subOrder.getMerchantCode());
            BusinessErrorInfoEnum.SYSTEM_ERROR.assertIsTrue(null != merchantRule && Objects.nonNull(merchantBO), "获取商户清分配置异常！");
            subOrder.setSubMerchantId(merchantBO.getSubAppid());
            subOrder.setEnterpriseCode(merchantBO.getEnterpriseCode());
            // 总订单金额
            final int totalSubOrderAmount = subOrder.getOrderAmount();
            // 计算平台佣金
            final Integer subOrderPlatformAmount = settlePlatformService.platformAmountCommissionMerchantSettleRule(totalSubOrderAmount, merchantRule);
            // 商户分账后实收金额
            final UnifiedOrderSettleBO settleBO = new UnifiedOrderSettleBO();
            settleBO.setFlowId(unifiedOrderBO.getFlowId());
            settleBO.setSubFlowId(subOrder.getSubFlowId());
            settleBO.setMerchantRegId(merchantBO.getMerchantRegId());
            settleBO.setMerchantCode(merchantBO.getMerchantCode());
            settleBO.setOrderAmount(subOrder.getOrderAmount());
            settleBO.setPlatformAmount(subOrderPlatformAmount);

            String settleFlowId = PaymentIdHelper.getSettleFlowId(settleBO.getSubFlowId(), orgPaymentChannelDetailBO.getChannelOrderIdPre());
            settleBO.setSettleFlowId(settleFlowId);
            // 明细项目
            int subFlowInc = 1; // 子订单流水起始值
            List<UnifiedOrderSettleBO.UnifiedOrderSettleItemBO> settleItems = new ArrayList<>();
            // 1、子商户
            UnifiedOrderSettleBO.UnifiedOrderSettleItemBO subMerchantItem = new UnifiedOrderSettleBO.UnifiedOrderSettleItemBO();
            subMerchantItem.setMerOrderId(PaymentIdHelper.getSubOrderFlowId(settleFlowId, subFlowInc));
            subMerchantItem.setIsPlatformMerchant(false);
            // 2、平台商户
            subMerchantItem.setMerchantRegId(merchantBO.getMerchantRegId());
            subMerchantItem.setMerchantCode(merchantBO.getMerchantCode());
            subMerchantItem.setMerchantName(subOrder.getMerchantName());
            subMerchantItem.setEnterpriseCode(merchantBO.getEnterpriseCode());
            subMerchantItem.setMid(merchantBO.getSubAppid());
            subMerchantItem.setTotalAmount(totalSubOrderAmount - subOrderPlatformAmount);
            subFlowInc++;
            settleItems.add(subMerchantItem);
            if (subOrderPlatformAmount > 0) {
                // 2、平台佣金金额>0，需要抽取佣金
                UnifiedOrderSettleBO.UnifiedOrderSettleItemBO platformMerchantItem = new UnifiedOrderSettleBO.UnifiedOrderSettleItemBO();
                platformMerchantItem.setMerOrderId(PaymentIdHelper.getSubOrderFlowId(settleFlowId, subFlowInc));
                platformMerchantItem.setIsPlatformMerchant(true);
                // 2、平台商户
                platformMerchantItem.setMerchantRegId(platformSubMerchant.getMerchantRegId());
                platformMerchantItem.setMerchantCode(platformSubMerchant.getMerchantCode());
                platformMerchantItem.setMerchantName(platformSubMerchant.getMerchantName());
                platformMerchantItem.setEnterpriseCode(platformSubMerchant.getEnterpriseCode());
                platformMerchantItem.setMid(platformSubMerchant.getSubAppid());
                platformMerchantItem.setTotalAmount(subOrderPlatformAmount);
                settleItems.add(platformMerchantItem);
            }
            settleBO.setSettleItems(settleItems);
            subSettleBOS.add(settleBO);
        }
        unifiedOrderBO.setSubSettles(subSettleBOS);
    }


    /**
     * 根据id获取支付流水
     *
     * @param id
     * @return
     */
    public PaymentOrder get(Long id) {
        return paymentOrderMapper.selectById(id);
    }

    /**
     * 根据id获取支付流水详情
     *
     * @param id
     * @return
     */
    public PaymentFlowQueryResVO getInfo(Long id) {
        PaymentOrder paymentOrder = paymentOrderMapper.selectById(id);
        PaymentFlowQueryResVO vo = ObjectConvertUtil.convert(paymentOrder, PaymentFlowQueryResVO.class).get();
        vo.setPaymentChannelCn(PaymentChannelEnum.getDesc(vo.getPaymentChannel()));
        vo.setPaymentWayCn(PaymentWayEnum.getDesc(vo.getPaymentWay()));
        vo.setPaySceneCn(PaySceneEnum.getDesc(vo.getPayScene()));
        vo.setPayStatusCn(FlowStatusEnum.getDesc(vo.getPayStatus()));
        return vo;
    }

    /**
     * 分页查询支付流水
     *
     * @param vo
     * @return
     */
    public PageInfo findList(PaymentFlowQueryVo vo) {
        int page = vo.getCurrentPage();
        int limit = vo.getPageSize();

        QueryWrapper<PaymentOrder> query = buildQuery(vo);
        query.orderByDesc("create_time");
        PageInfo<PaymentOrder> pageInfo = paymentOrderMapper.find(query, page, limit);
        PageInfo<PaymentFlowQueryResVO> pageRes = new PageInfo<>();
        List<PaymentFlowQueryResVO> vos = pageInfo.getList().stream().map(flow ->
                ObjectConvertUtil.convert(flow, PaymentFlowQueryResVO.class).get()).collect(Collectors.toList());
        vos.forEach(obj -> {
            obj.setPaymentChannelCn(PaymentChannelEnum.getDesc(obj.getPaymentChannel()));
            obj.setPaymentWayCn(PaymentWayEnum.getDesc(obj.getPaymentWay()));
            obj.setPaySceneCn(PaySceneEnum.getDesc(obj.getPayScene()));
            obj.setPayStatusCn(FlowStatusEnum.getDesc(obj.getPayStatus()));
        });
        pageRes.setList(vos);
        pageRes.setPagination(pageInfo.getPagination());
        return pageRes;
    }

    // 构造查询条件
    private QueryWrapper<PaymentOrder> buildQuery(PaymentFlowQueryVo vo) {
        QueryWrapper<PaymentOrder> query = new QueryWrapper<>();

        if (StringUtils.isNotBlank(vo.getFlowId())) {
            query.eq("flow_id", vo.getFlowId());
        }

        if (StringUtils.isNotBlank(vo.getPaymentId())) {
            query.eq("payment_id", vo.getPaymentId());
        }
        if (StringUtils.isNotBlank(vo.getTradeNo())) {
            query.eq("trade_no", vo.getTradeNo());
        }
        if (StringUtils.isNotBlank(vo.getPayStatus())) {
            query.eq("pay_status", vo.getPayStatus());
        }

        if (StringUtils.isNotBlank(vo.getPaymentChannel())) {
            query.eq("payment_channel", vo.getPaymentChannel());
        }
        if (StringUtils.isNotBlank(vo.getPaymentWay())) {
            query.eq("payment_way", vo.getPaymentWay());
        }

        if (vo.getFlowTimeBegin() != null) {
            query.ge("flow_time", vo.getFlowTimeBegin());
        }
        if (vo.getFlowTimeEnd() != null) {
            query.lt("flow_time", vo.getFlowTimeEnd().plusDays(1));
        }
        return query;
    }

    /**
     * 功能描述: 关闭订单
     *
     * @param:
     * @return:
     * @auther: LiuDong
     * @date: 10:20 2024/3/4
     */
    @Override
    public Result<String> closeOrder(CloseOrderVO closeOrderVO) throws BusinessException {
        QueryWrapper<PaymentOrder> conditions = new QueryWrapper<>();
        //conditions.eq("user_id", closeOrderVO.getUid());
        conditions.eq("flow_id", closeOrderVO.getFlowId());

        PaymentOrder paymentOrder = paymentOrderMapper.selectOne(conditions);
        BusinessErrorInfoEnum.CHANNEL_NO_FLOW.assertNotNull(paymentOrder);
        if (paymentOrder.getPayStatus().equals(FlowStatusEnum.SUCCESS.getCode())) {
            //订单已支付，不允许关闭，返回状态码给订单中心
            final PayNotifyBO payNotifyBO = new PayNotifyBO();
            payNotifyBO.setPaymentId(paymentOrder.getPaymentId());
            payNotifyBO.setFlowId(paymentOrder.getFlowId());
            payNotifyBO.setUserId(paymentOrder.getUserId());
            payNotifyBO.setPayTime(paymentOrder.getFlowTime());
            payNotifyBO.setStatus(paymentOrder.getPayStatus());
            payNotifyBO.setAmount(paymentOrder.getAmount());
            payNotifyBO.setTransactionId(paymentOrder.getTransactionId());
            payNotifyBO.setTradeNo(paymentOrder.getTradeNo());
            payNotifyBO.setOrderApp(paymentOrder.getOrderApp());
            payNotifyBO.setPaymentChannel(paymentOrder.getPaymentChannel());
            payNotifyBO.setPaymentWay(paymentOrder.getPaymentWay());
            payNotifyBO.setPayScene(paymentOrder.getPayScene());
            payNotifyBO.setCouponAmount(paymentOrder.getDiscount());

            List<PayNotifySubBO> payNotifySubBOS = new ArrayList<>();
            for (PaymentFlowDetail flowDetail : paymentFlowDetailService.findByFlowId(paymentOrder.getFlowId())) {
                final PayNotifySubBO payNotifySubBO = new PayNotifySubBO();
                payNotifySubBO.setSubOrderId(flowDetail.getSubOrderId());
                payNotifySubBO.setFlowId(flowDetail.getFlowId());
                payNotifySubBO.setSubFlowId(flowDetail.getSubFlowId());
                payNotifySubBO.setAmount(flowDetail.getAmount());
                payNotifySubBO.setDiscount(flowDetail.getDiscount());
                payNotifySubBO.setChannelDiscount(flowDetail.getChannelDiscount());
                payNotifySubBOS.add(payNotifySubBO);
            }
            payNotifyBO.setSubFlows(payNotifySubBOS);
            notifyService.sendPayFlowMessage(payNotifyBO, paymentOrder.getPayStatus(), paymentOrder.getChannelDiscount() == null ? 0 : paymentOrder.getChannelDiscount());
            BusinessErrorInfoEnum.CHANNEL_CLOSE_PAID_ERROR.assertFail();
        }
        BusinessErrorInfoEnum.CHANNEL_CLOSE_STATUS_ERROR.assertIsTrue(FlowStatusEnum.WAITING.getCode().equals(paymentOrder.getPayStatus()));

        final OrgPaymentChannelDetailBO orgPaymentChannelDetailBO = orgPaymentChannelService.queryAllOrgPaymentChannelInfoById(paymentOrder.getOrgPaymentChannelId());
        AbstractPayChannel payChannel = PayChannelFactory.get(paymentOrder.getPaymentChannel());
        CloseOrderBO closeOrderBO = new CloseOrderBO();
        closeOrderBO.setUid(paymentOrder.getUserId());
        closeOrderBO.setOrderId(paymentOrder.getFlowId());
        closeOrderBO.setTransactionId(paymentOrder.getTransactionId());

        closeOrderBO.setOrgPaymentChannelId(paymentOrder.getOrgPaymentChannelId());
        closeOrderBO.setChannelOrderIdPre(orgPaymentChannelDetailBO.getChannelOrderIdPre());
        closeOrderBO.setBusinessCode(paymentOrder.getBusinessCode());
        closeOrderBO.setSlottingRate(orgPaymentChannelDetailBO.getSlottingRate());
        closeOrderBO.setPaymentChannelName(orgPaymentChannelDetailBO.getPaymentChannelName());

        Result result = payChannel.close(closeOrderBO);
        if (result.getRet() == 0) {
            PaymentOrder updateFlow = new PaymentOrder();
            updateFlow.setPayStatus(FlowStatusEnum.FAIL.getCode());
            paymentOrderMapper.update(updateFlow, conditions);
            PaymentCache.delCachePaymentStatusQueryParams(closeOrderVO.getFlowId());
        } else {
            // TODO 这里需要处理一下关闭订单银行返回已支付的情况
        }
        Result<String> r = new Result<String>(closeOrderVO.getFlowId());
        r.setRet(result.getRet());
        r.setErrorCode(result.getErrorCode());
        r.setMsg(result.getRet() == 0 ? "关闭支付订单成功！" : "关闭支付订单失败！");
        return r;
    }

    /**
     * 功能描述:核销接口
     *
     * @param:
     * @return:
     * @auther: LiuDong
     * @date: 10:20 2024/3/4
     */
    @Override
    public Result<VerificationOrderResponseVO> verification(VerificationRequestVO orderVO) throws BusinessException {
        Set<String> subFlowIds = orderVO.getSubFlowIds();
        PaymentOrder paymentOrder = paymentOrderMapper.selectOne(new LambdaQueryWrapper<PaymentOrder>().eq(PaymentOrder::getFlowId, orderVO.getFlowId()));
        Asserts.isNotNull(paymentOrder, "核销失败，未查询到支付信息！");
        List<PaymentFlowDetailSettle> paymentFlowDetailSettles = flowDetailSettleService.queryBySubFlowIds(orderVO.getFlowId(), subFlowIds);
        Asserts.isTrue(CollectionUtils.isNotEmpty(paymentFlowDetailSettles), "核销失败，未查询到结算信息！");
        Set<String> success = new HashSet<>();
        Set<String> error = new HashSet<>();
        // 根据清分规则计算应清日期
        VerificationOrderResponseVO result = new VerificationOrderResponseVO();
        List<PaymentFlowDetailSettle> waitSettleSubFlows = new ArrayList<>();
        for (PaymentFlowDetailSettle paymentFlowDetailSettle : paymentFlowDetailSettles) {
            if (paymentFlowDetailSettle.getSettleStatus().equals(SettleStatusEnum.ING.getCode()) || paymentFlowDetailSettle.getSettleStatus().equals(SettleStatusEnum.SUCCESS.getCode())) {
                // 核销成功
                success.add(paymentFlowDetailSettle.getSubFlowId());
                continue;
            }
            // 等待执行核销逻辑
            waitSettleSubFlows.add(paymentFlowDetailSettle);
        }
        if (CollectionUtils.isEmpty(waitSettleSubFlows)) {
            // 全部都已经核销过，返回
            result.setSuccess(orderVO.getSubFlowIds());
            return Result.success(result);
        }
        // 3、查询清分规则
        final Map<String, SettlementRuleResVO> rules = settlePlatformService.querySettleRules(waitSettleSubFlows.stream().map(PaymentFlowDetailSettle::getSettleRuleCode).collect(Collectors.toSet()));
        if (MapUtils.isEmpty(rules)) {
            log.error("核销失败，缺少清分规则配置！");
            result.setSuccess(success);
            result.setError(subFlowIds);
            return Result.success(result);
        }
        List<PaymentFlowDetailSettle> updSettles = new ArrayList<>();
        for (PaymentFlowDetailSettle waitSettleSubFlow : waitSettleSubFlows) {
            final SettlementRuleResVO rule = rules.get(waitSettleSubFlow.getSettleRuleCode());
            if (Objects.isNull(rule) || Objects.isNull(rule.getSettleRuleCode())) {
                // 缺少清分规则
                error.add(waitSettleSubFlow.getSubFlowId());
                continue;
            }
            // 执行核销逻辑
            PaymentFlowDetailSettle updItem = new PaymentFlowDetailSettle();
            updItem.setId(waitSettleSubFlow.getId());
            updItem.setVerificationId(orderVO.getVerificationId());
            updItem.setVerificationStatus(VerificationStatusEnum.SC.getCode());
            updItem.setVerificationTime(LocalDateTime.now());
            updItem.setSettleDate(calSettleDate(rule, LocalDate.now()));
            updItem.setSubFlowId(waitSettleSubFlow.getSubFlowId());
            updSettles.add(updItem);
        }
        if (CollectionUtils.isEmpty(updSettles)) {
            result.setSuccess(success);
            result.setError(error);
            return Result.success(result);
        }
        AbstractPayChannel payChannel = PayChannelFactory.get(paymentOrder.getPaymentChannel());
        Boolean isSc = transactionTemplate.execute(transactionStatus -> {
            try {
                // 调用支付通道执行核销逻辑，如果要在核销时候触发的话
                List<PaymentFlowDetailSettle> detailSettles = flowDetailSettleService.queryBySubFlowIds(orderVO.getFlowId(), updSettles.stream().map(PaymentFlowDetailSettle::getSubFlowId).collect(Collectors.toSet()));
                List<PaymentFlowDetailSettleDetail> detailSettlesDetails = flowDetailSettleDetailService.list(new LambdaQueryWrapper<PaymentFlowDetailSettleDetail>().in(PaymentFlowDetailSettleDetail::getSettleFlowId, detailSettles.stream().map(PaymentFlowDetailSettle::getSettleFlowId).toList()));
                Result channelSettleResult = payChannel.settleV2(detailSettles, detailSettlesDetails);
                Asserts.isTrue(null != channelSettleResult && channelSettleResult.getRet() == 0, "渠道核销失败！");
                log.info("订单核销成功！");
                success.addAll(updSettles.stream().map(PaymentFlowDetailSettle::getSubFlowId).toList());
                flowDetailSettleService.updateBatchById(updSettles);
                return true;
            } catch (Exception e) {
                transactionStatus.setRollbackOnly();
                throw e;
            }
        });
        if (null != isSc && isSc) {
            result.setSuccess(success);
        } else {
            error.addAll(success);
        }
        result.setError(error);
        return Result.success(result);
    }

    /**
     * 根据清分规则计算渠道应核销日期
     *
     * @param settlementRuleResVO {@link SettlementRuleResVO} 清分规则
     * @param startDate           {@link LocalDate} 计算的基点日期
     * @return {@link LocalDate}
     */
    private LocalDate calSettleDate(SettlementRuleResVO settlementRuleResVO, LocalDate startDate) {
        LocalDate settleDate;
        // 清分周期类型-按天
        if (ClearingCycleModeEnum.DATE.getCode().equalsIgnoreCase(settlementRuleResVO.getClearingCycleMode())) {
            settleDate = startDate.plusDays(settlementRuleResVO.getClearingCycleValue());
        } else { // 清分周期类型-按月
            settleDate = startDate.plusMonths(settlementRuleResVO.getClearingCycleValue());
            settleDate = settleDate.withDayOfMonth(settlementRuleResVO.getClearingCycleValueExt());
        }
        return settleDate;
    }

    @Override
    public PaymentOrder findByPaymentFlowId(String flowId) {
        QueryWrapper<PaymentOrder> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("flow_id", flowId);
        PaymentOrder paymentOrder = paymentOrderMapper.selectOne(queryWrapper);
        return paymentOrder;
    }

    @Override
    public PaymentOrder get(String userId, String flowId) {
        QueryWrapper<PaymentOrder> conditions = new QueryWrapper<>();
        conditions.eq("user_id", userId);
        conditions.eq("flow_id", flowId);
        return paymentOrderMapper.selectOne(conditions);
    }

    @Override
    public PaymentOrder get(String flowId) {
        QueryWrapper<PaymentOrder> conditions = new QueryWrapper<>();
        conditions.eq("flow_id", flowId);
        return paymentOrderMapper.selectOne(conditions);
    }

    @Override
    public PayNotifyBO payResult(PayResultBO payResultBO) throws BusinessException {
        try (Lock lock = new Lock(Lock.LOCK_CHANNEL_CALLBACK_PAY, payResultBO.getOrderId(), 0, 5)) {
            // 针对流水号加锁，如果并发重复通知，未获取锁的抛出重复异常
            BusinessErrorInfoEnum.CHANNEL_CALLBACK_PAY_DUP.assertIsTrue(lock.getLockRes());

            QueryWrapper<PaymentOrder> conditions = new QueryWrapper<>();
            if (StringUtils.isNotEmpty(payResultBO.getUid())) {
                conditions.eq("user_id", payResultBO.getUid());
            }
            conditions.eq("flow_id", payResultBO.getOrderId());

            PaymentOrder paymentOrder = paymentOrderMapper.selectOne(conditions);
            BusinessErrorInfoEnum.CHANNEL_CALLBACK_PAY_NO_FLOW.assertNotNull(paymentOrder);
            BusinessErrorInfoEnum.CHANNEL_CALLBACK_PAY_SUCCESS_DUP.assertIsTrue(paymentOrder.getPayStatus().equals(FlowStatusEnum.WAITING.getCode()));
            // 判断渠道返回实付金额是否一致
            if (payResultBO.getStatus().equals(FlowStatusEnum.SUCCESS.getCode())) {
                BusinessErrorInfoEnum.CHANNEL_CALLBACK_PAY_AMOUNT_DIFF.assertIsTrue(
                        (payResultBO.getAmount() + payResultBO.getCouponAmount()) == paymentOrder.getAmount());
            }
            PaymentOrder updatePayment = new PaymentOrder();
            updatePayment.setTradeNo(payResultBO.getTradeNo());
            updatePayment.setPayStatus("success".equals(payResultBO.getStatus()) ? FlowStatusEnum.SUCCESS.getCode() : FlowStatusEnum.FAIL.getCode());
            updatePayment.setFlowTime(payResultBO.getPayTime());
            // 修改子订单实收金额
            updatePayment.setAmount(payResultBO.getAmount());
            if (StringUtils.isNotEmpty(payResultBO.getTransactionId())) {
                updatePayment.setTransactionId(payResultBO.getTransactionId());
            }

            final List<PayNotifySubBO> payNotifySubBOS = paymentFlowDetailService.updateAmount(payResultBO.getOrderId(), payResultBO.getCouponAmount());
            // 修改子流水的金额
            if (null != payResultBO.getCouponAmount() && payResultBO.getCouponAmount() > 0) {
                log.info("银联渠道支付回调 更新流水优惠信息 orderId:{} 优惠金额：{}", payResultBO.getOrderId(), payResultBO.getCouponAmount());
                updatePayment.setDiscount(paymentOrder.getDiscount() + payResultBO.getCouponAmount());
                updatePayment.setChannelDiscount(payResultBO.getCouponAmount());
            }
            paymentOrderMapper.update(updatePayment, conditions);

            final PayNotifyBO payNotifyBO = new PayNotifyBO();
            payNotifyBO.setPaymentId(paymentOrder.getPaymentId());
            payNotifyBO.setFlowId(paymentOrder.getFlowId());
            payNotifyBO.setUserId(paymentOrder.getUserId());
            payNotifyBO.setPayTime(payResultBO.getPayTime());
            payNotifyBO.setStatus(updatePayment.getPayStatus());
            payNotifyBO.setAmount(payResultBO.getAmount());
            payNotifyBO.setTransactionId(updatePayment.getTransactionId());
            payNotifyBO.setTradeNo(updatePayment.getTradeNo());
            payNotifyBO.setOrderApp(paymentOrder.getOrderApp());
            payNotifyBO.setPaymentChannel(paymentOrder.getPaymentChannel());
            payNotifyBO.setPaymentWay(paymentOrder.getPaymentWay());
            payNotifyBO.setPayScene(paymentOrder.getPayScene());
            payNotifyBO.setCouponAmount(payResultBO.getCouponAmount());
            payNotifyBO.setSubFlows(payNotifySubBOS);
            return payNotifyBO;
        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            throw new BusinessException(BusinessErrorInfoEnum.CHANNEL_CALLBACK_PAY_ERROR, null, e.getMessage(), e);
        }
    }

    @Override
    public long totalAmountOfMerchant(String merchantCode, LocalDateTime startTime, LocalDateTime endTime, String paymentChannel) {
        return paymentOrderMapper.totalAmountOfMerchant(merchantCode, startTime, endTime, paymentChannel);
    }

    @Override
    public Result<GetUnifiedOrderResBO> queryPayResult(GetUnifiedOrderVo getUnifiedOrderVo) {
        if (StringUtils.isEmpty(getUnifiedOrderVo.getFlowId())) {
            return Result.fail("flow_id不允许为空");
        }
        QueryWrapper<PaymentOrder> conditions = new QueryWrapper<>();
        //conditions.eq("user_id", getUnifiedOrderVo.getUid());
        conditions.eq("flow_id", getUnifiedOrderVo.getFlowId());
        PaymentOrder paymentOrder = paymentOrderMapper.selectOne(conditions);
        BusinessErrorInfoEnum.CHANNEL_CALLBACK_PAY_NO_FLOW.assertNotNull(paymentOrder);

        if (paymentOrder.getPayStatus().equals(FlowStatusEnum.WAITING.getCode())) {
            // 待支付状态
            AbstractPayChannel payChannel = PayChannelFactory.get(paymentOrder.getPaymentChannel());
            GetUnifiedOrderBO getUnifiedOrderBO = new GetUnifiedOrderBO();
            getUnifiedOrderBO.setOrderId(getUnifiedOrderVo.getFlowId());
            getUnifiedOrderBO.setUid(paymentOrder.getUserId());
            getUnifiedOrderBO.setTransactionId(getUnifiedOrderVo.getTransactionId());
            getUnifiedOrderBO.setOrgPaymentChannelId(paymentOrder.getOrgPaymentChannelId());
            Result<GetUnifiedOrderResBO> getUnifiedOrderResBOResult = payChannel.get(getUnifiedOrderBO);
            log.info("查询支付渠道订单支付结果返回：{}", getUnifiedOrderResBOResult.toString());
            /*if (getUnifiedOrderResBOResult.getRet() == 0) {
                GetUnifiedOrderResBO data = getUnifiedOrderResBOResult.getData();
                if (data != null && (data.getOrderStatus().equals(ChannelOrderStatusEnum.fail)) || data.getOrderStatus().equals(ChannelOrderStatusEnum.success)) {
                    PaymentOrder updatePayment = new PaymentOrder();
                    updatePayment.setTradeNo(data.getTradeNo());
                    updatePayment.setPayStatus("success".equals(data.getOrderStatus().name()) ? FlowStatusEnum.SUCCESS.getCode() : FlowStatusEnum.FAIL.getCode());
                    updatePayment.setFlowTime(data.getPayTime());
                    updatePayment.setAmount(data.getAmount());
                    paymentOrderMapper.update(updatePayment, conditions);
                    //TODO 推送结果
                }
            }*/
            return getUnifiedOrderResBOResult;
        } else {
            GetUnifiedOrderResBO getUnifiedOrderResBO = new GetUnifiedOrderResBO();
            getUnifiedOrderResBO.setAmount(paymentOrder.getAmount());
            getUnifiedOrderResBO.setOrderId(getUnifiedOrderVo.getFlowId());
            getUnifiedOrderResBO.setOrderStatus(ChannelOrderStatusEnum.getByCode(paymentOrder.getPayStatus()));
            getUnifiedOrderResBO.setPayTime(paymentOrder.getFlowTime());
            getUnifiedOrderResBO.setTradeNo(paymentOrder.getTradeNo());
            getUnifiedOrderResBO.setTransactionId(paymentOrder.getTransactionId());
            return Result.success(getUnifiedOrderResBO);
        }
    }

    @Override
    public Result exportList(PaymentFlowQueryVo vo, HttpServletResponse response) throws IOException {
        QueryWrapper<PaymentOrder> query = buildQuery(vo);
        query.orderByAsc("create_time");
        if (vo.getFlowTimeBegin() == null || vo.getFlowTimeEnd() == null) {
            DateTime now = new DateTime();
            query.ge("flow_time", DateUtil.beginOfMonth(now));
            query.lt("flow_time", DateUtil.endOfMonth(now));
        }
        query.select("payment_id", "transaction_id", "trade_no", "order_amount", "amount", "payment_channel", "payment_way", "pay_status", "create_time", "flow_time");
        List<PaymentOrder> orders = paymentOrderMapper.selectList(query);
        if (orders.isEmpty()) return Result.success("当前查询条件下暂无数据");

        // 定义表头
        List<List<String>> head = new ArrayList<>();
        head.add(Collections.singletonList("序号"));
        head.add(Collections.singletonList("支付订单号"));
        head.add(Collections.singletonList("渠道订单号"));
        head.add(Collections.singletonList("收单机构订单号"));
        head.add(Collections.singletonList("订单应收金额(元)"));
        head.add(Collections.singletonList("实收金额(元)"));
        head.add(Collections.singletonList("支付渠道"));
        head.add(Collections.singletonList("支付方式"));
        head.add(Collections.singletonList("支付状态"));
        head.add(Collections.singletonList("订单创建时间"));
        head.add(Collections.singletonList("支付时间"));

        // 定义数据
        List<List<Object>> data = new ArrayList<>();
        orders.forEach(order -> {
            LocalDateTime createTime = order.getCreateTime();
            LocalDateTime flowTime = order.getFlowTime();
            data.add(Arrays.asList(
                    orders.indexOf(order) + 1,
                    order.getPaymentId(),
                    order.getTransactionId(),
                    order.getTradeNo(),
                    NumberUtil.div(Optional.ofNullable(order.getOrderAmount()).orElse(0), Integer.valueOf(100)),
                    NumberUtil.div(Optional.ofNullable(order.getAmount()).orElse(0), Integer.valueOf(100)),
                    PaymentChannelEnum.getDesc(order.getPaymentChannel()),
                    PaymentWayEnum.getDesc(order.getPaymentWay()),
                    FlowStatusEnum.getDesc(order.getPayStatus()),
                    createTime != null ? createTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")) : "无",
                    flowTime != null ? flowTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")) : "无"
            ));
        });

        ExcelBuildUtil.mapToExcel(head, data, response);
        return Result.success("导出成功");
    }

    @Override
    public UnifiedOrderResVO appleInAppPay(OrderPayDTO unifiedOrderVO) {
        // 检查支付渠道、方式是否匹配
        BusinessErrorInfoEnum.CHANNEL_PAY_FAIL.assertIsTrue(StringUtils.isNotEmpty(unifiedOrderVO.getTransactionId())
                && StringUtils.isNotEmpty(unifiedOrderVO.getReceiptData()));
        BusinessErrorInfoEnum.CHANNEL_NO_PAY_WAY.assertNotNull(EnumUtil.getEnumByCode(PaymentWayEnum.class, unifiedOrderVO.getPayWay()));
        BusinessErrorInfoEnum.CHANNEL_NO_PAY_SCENE.assertNotNull(EnumUtil.getEnumByCodeIgnoreCase(PaySceneEnum.class, unifiedOrderVO.getPayScene()));
        try (Lock lock = new Lock(Lock.LOCK_CHANNEL_PAY, unifiedOrderVO.getPaymentId(), 0, 5)) {
            BusinessErrorInfoEnum.CHANNEL_PAY_DUP.assertIsTrue(lock.getLockRes());

            QueryWrapper<PaymentOrder> conditions = new QueryWrapper<>();
            conditions.eq("payment_id", unifiedOrderVO.getPaymentId());
            conditions.eq("pay_status", FlowStatusEnum.SUCCESS.getCode());
            BusinessErrorInfoEnum.CHANNEL_PAY_DUP.assertIsTrue(paymentOrderMapper.selectCount(conditions) == 0);
            final OrgPaymentChannelDetailBO orgPaymentChannelDetailBO = orgPaymentChannelService.queryAllOrgPaymentChannelInfoById(unifiedOrderVO.getOrgPaymentChannelId());
            String flowId = PaymentIdHelper.getPay(unifiedOrderVO.getUid(), PaymentChannelEnum.APPLE.getCode(), null, orgPaymentChannelDetailBO.getChannelOrderIdPre());

            PaymentOrder paymentOrder = ObjectConvertUtil.convert(unifiedOrderVO, PaymentOrder.class).get();
            paymentOrder.setPaymentChannel(PaymentChannelEnum.APPLE.getCode());
            paymentOrder.setDiscount(0);
            paymentOrder.setChannelDiscount(0);
            paymentOrder.setPlatformDiscount(0);

            unifiedOrderVO.getSubOrderPayDTOS().forEach(unifiedSubOrderVO -> {
                unifiedSubOrderVO.setPlatformDiscount(0);
            });
            paymentOrder.setUserId(unifiedOrderVO.getUid());
            paymentOrder.setFlowId(flowId);
            paymentOrder.setPayStatus(FlowStatusEnum.WAITING.getCode());
            paymentOrder.setPaymentWay(unifiedOrderVO.getPayWay());
            paymentOrder.setOrderApp(unifiedOrderVO.getBusinessType());
            paymentOrder.setUserTel(unifiedOrderVO.getUserTel());
            paymentOrder.setSlottingRate(new BigDecimal(0));

            paymentOrderMapper.insert(paymentOrder);

            final Map<String, SettlementRuleResVO> merchantRuleMap = settleRulesCache.querySettleRules(unifiedOrderVO.getOrgPaymentChannelId(),
                    unifiedOrderVO.getSubOrderPayDTOS().stream().map(OrderPayDTO.SubOrderPayDTO::getMerchantCode).collect(Collectors.toList()));
            BusinessErrorInfoEnum.CHANNEL_PAY_FAIL.assertIsTrue(null != merchantRuleMap && merchantRuleMap.size() > 0);
            paymentFlowDetailService.save(flowId, paymentOrder.getPaymentId(), unifiedOrderVO, merchantRuleMap);

            final ApplePayChannel payChannel = PayChannelFactory.getApplePayChannel();
            final Result result = payChannel.unifiedOrder2Queue(new ApplePayResultCheckBO(unifiedOrderVO.getTransactionId(),
                    unifiedOrderVO.getReceiptData(), 1, flowId, unifiedOrderVO.getOrderAmount()));

            // 此方法没有事务，可以直接抛出支付失败异常
            BusinessErrorInfoEnum.CHANNEL_PAY_FAIL.assertIsTrue(result.getRet() == 0);

            UnifiedOrderResVO unifiedOrderResVO = new UnifiedOrderResVO();
            unifiedOrderResVO.setFlowId(flowId);
            unifiedOrderResVO.setPaymentId(unifiedOrderVO.getPaymentId());
            unifiedOrderResVO.setAmount(unifiedOrderVO.getAmount());
            unifiedOrderResVO.setPayInfo("");
            unifiedOrderResVO.setSlottingRate(paymentOrder.getSlottingRate());
            return unifiedOrderResVO;
        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            throw new BusinessException(BusinessErrorInfoEnum.CHANNEL_PAY_FAIL, null, e.getMessage(), e);
        }
    }

    @Override
    public UnifiedOrderResVO appleInAppPayV2(OrderPayDTO unifiedOrderVO) {
        // 检查支付渠道、方式是否匹配
        BusinessErrorInfoEnum.CHANNEL_PAY_FAIL.assertIsTrue(StringUtils.isNotEmpty(unifiedOrderVO.getTransactionId())
                && StringUtils.isNotEmpty(unifiedOrderVO.getReceiptData()));
        BusinessErrorInfoEnum.CHANNEL_NO_PAY_WAY.assertNotNull(EnumUtil.getEnumByCode(PaymentWayEnum.class, unifiedOrderVO.getPayWay()));
        BusinessErrorInfoEnum.CHANNEL_NO_PAY_SCENE.assertNotNull(EnumUtil.getEnumByCodeIgnoreCase(PaySceneEnum.class, unifiedOrderVO.getPayScene()));
        try (Lock lock = new Lock(Lock.LOCK_CHANNEL_PAY, unifiedOrderVO.getPaymentId(), 0, 5)) {
            BusinessErrorInfoEnum.CHANNEL_PAY_DUP.assertIsTrue(lock.getLockRes());

            QueryWrapper<PaymentOrder> conditions = new QueryWrapper<>();
            conditions.eq("payment_id", unifiedOrderVO.getPaymentId());
            conditions.eq("pay_status", FlowStatusEnum.SUCCESS.getCode());
            BusinessErrorInfoEnum.CHANNEL_PAY_DUP.assertIsTrue(paymentOrderMapper.selectCount(conditions) == 0);
            final OrgPaymentChannelDetailBO orgPaymentChannelDetailBO = orgPaymentChannelService.queryAllOrgPaymentChannelInfoById(unifiedOrderVO.getOrgPaymentChannelId());
            String flowId = PaymentIdHelper.getPay(unifiedOrderVO.getUid(), PaymentChannelEnum.APPLE.getCode(), null, orgPaymentChannelDetailBO.getChannelOrderIdPre());

            PaymentOrder paymentOrder = ObjectConvertUtil.convert(unifiedOrderVO, PaymentOrder.class).get();
            paymentOrder.setPaymentChannel(PaymentChannelEnum.APPLE.getCode());
            paymentOrder.setDiscount(0);
            paymentOrder.setChannelDiscount(0);
            paymentOrder.setPlatformDiscount(0);

            unifiedOrderVO.getSubOrderPayDTOS().forEach(unifiedSubOrderVO -> {
                unifiedSubOrderVO.setPlatformDiscount(0);
            });
            paymentOrder.setUserId(unifiedOrderVO.getUid());
            paymentOrder.setFlowId(flowId);
            paymentOrder.setPayStatus(FlowStatusEnum.WAITING.getCode());
            paymentOrder.setPaymentWay(unifiedOrderVO.getPayWay());
            paymentOrder.setOrderApp(unifiedOrderVO.getBusinessType());
            paymentOrder.setUserTel(unifiedOrderVO.getUserTel());
            paymentOrder.setSlottingRate(new BigDecimal(0));

            final ApplePayChannel payChannel = PayChannelFactory.getApplePayChannel();
            final Result result = payChannel.unifiedOrderV2(new ApplePayResultCheckBO(unifiedOrderVO.getTransactionId(),
                    unifiedOrderVO.getReceiptData(), 1, flowId, unifiedOrderVO.getOrderAmount()));
            paymentOrder.setTradeNo(unifiedOrderVO.getPaymentId());
            paymentOrder.setPayStatus(result != null && result.getRet() == 0 ? FlowStatusEnum.SUCCESS.getCode() : FlowStatusEnum.FAIL.getCode());
            paymentOrder.setFlowTime(LocalDateTime.now());
            // 修改子订单实收金额
            paymentOrder.setAmount(unifiedOrderVO.getOrderAmount());
            paymentOrderMapper.insert(paymentOrder);
            final Map<String, SettlementRuleResVO> merchantRuleMap = settleRulesCache.querySettleRules(unifiedOrderVO.getOrgPaymentChannelId(),
                    unifiedOrderVO.getSubOrderPayDTOS().stream().map(OrderPayDTO.SubOrderPayDTO::getMerchantCode).collect(Collectors.toList()));
            BusinessErrorInfoEnum.CHANNEL_PAY_FAIL.assertIsTrue(null != merchantRuleMap && merchantRuleMap.size() > 0);
            paymentFlowDetailService.save(flowId, paymentOrder.getPaymentId(), unifiedOrderVO, merchantRuleMap);

            // 此方法没有事务，可以直接抛出支付失败异常
            BusinessErrorInfoEnum.CHANNEL_PAY_FAIL.assertIsTrue(result.getRet() == 0);

            UnifiedOrderResVO unifiedOrderResVO = new UnifiedOrderResVO();
            unifiedOrderResVO.setFlowId(flowId);
            unifiedOrderResVO.setPaymentId(unifiedOrderVO.getPaymentId());
            unifiedOrderResVO.setAmount(unifiedOrderVO.getAmount());
            unifiedOrderResVO.setPayInfo("");
            unifiedOrderResVO.setSlottingRate(paymentOrder.getSlottingRate());
            return unifiedOrderResVO;
        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            throw new BusinessException(BusinessErrorInfoEnum.CHANNEL_PAY_FAIL, null, e.getMessage(), e);
        }
    }

    @Override
    public PaymentOrder querySuccessFlowByPaymentId(String paymentId) {
        QueryWrapper<PaymentOrder> conditions = new QueryWrapper<>();
        conditions.eq("payment_id", paymentId);
        conditions.eq("pay_status", FlowStatusEnum.SUCCESS.getCode());
        return paymentOrderMapper.selectOne(conditions);
    }
}
